﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;

namespace WorldServer
{    
    public partial class FormMainForm : Form
    {
        public delegate void UpdateStatusDelegate(string s);
        Thread listener = null;
        List<NetworkSocket> nwsList = null;
        public UpdateStatusDelegate updateStatusDelegate1;
        volatile bool _stopListenForRequests;

        public FormMainForm()
        {
            InitializeComponent();
            nwsList = new List<NetworkSocket>();
            updateStatusDelegate1 = new UpdateStatusDelegate(updateStatus);
        }

        void ListenForRequests()
        {
            NetworkSocket nws;

            nws = new NetworkSocket(this);
            nws.Create();
            _stopListenForRequests = false;

            while (!_stopListenForRequests)
            {
                NetworkSocket nwsnew = nws.Accept();

                if (nwsnew != null)
                {
                    nwsList.Add(nwsnew);
                    // invoke a method passing networksocket, handle sockets in round robin in main()
                    rtbStatus.Invoke(updateStatusDelegate1,
                        new object[] { "New connection\n" })  ;
                    byte[] msg = Encoding.UTF8.GetBytes("Hello!");
                    nwsnew.Send(msg);
                }

                try 
                {
                    foreach (NetworkSocket nwsinlist in nwsList)
                    {
                        if (nwsinlist.PollError())
                        {
                            rtbStatus.Invoke(updateStatusDelegate1,
                                new object[] { "Connection Lost\n" });
                            nwsinlist.Dispose();
                            nwsList.Remove(nwsinlist);
                        }

                        if (nwsinlist.PollRead())
                        {
                            int count;
                            byte[] msg = new byte[1024];
                            count = nwsinlist.Receive(msg,1024);
                            if (count == 0)
                            {
                                rtbStatus.Invoke(updateStatusDelegate1,
                                    new object[] { "Connection Lost\n" });
                                nwsinlist.Dispose();
                                nwsList.Remove(nwsinlist);
                            }
                            else
                            {
                                rtbStatus.Invoke(updateStatusDelegate1,
                                    Encoding.ASCII.GetString(msg));
                                foreach (NetworkSocket nwssend in nwsList)
                                {
                                    nwssend.Send(msg);
                                }
                            }
                        }
                    }
                }
                catch(InvalidOperationException)
                {
                }
            }
            // dispose of main network socket 
            nws.Dispose();
            nws = null;

            // close all connections and dispose of sockets
            foreach (NetworkSocket nwsinlist in nwsList)
            {
                nwsinlist.Dispose();
            }
            nwsList.Clear();
            // update status
            rtbStatus.Invoke(updateStatusDelegate1,
                new object[] { "Listener Thread Stopped\n" });

        }

        private void btnStartServer_Click(object sender, EventArgs e)
        {
            StartServer();
        }

        private void btnRestartServer_Click(object sender, EventArgs e)
        {
            StopServer();
            // needed to allow the server to stop and release main listener socket
            while (listener.IsAlive)
            {
                Application.DoEvents();
            }
            StartServer();
        }

        private void btnStopServer_Click(object sender, EventArgs e)
        {
            StopServer();
        }

        public void updateStatus(string str)
        {
            rtbStatus.Text = rtbStatus.Text + str;
        }

        private void StartServer()
        {
            btnStartServer.Enabled = false;
            btnRestartServer.Enabled = true;
            btnStopServer.Enabled = true;

            updateStatus("Server Started\n");
 
            setProgress(10); // set progress bar to 10%
            // start networking
            listener = new Thread(new ThreadStart(ListenForRequests));
            listener.IsBackground = true;
            listener.Start();

            setProgress(30); // set progress bar to 30%
            // start database
 
            setProgress(50); // set progress bar to 50%
            // start game engine

            setProgress(70); // set progress bar to 70%
            // start ticker

            setProgress(90); // set progress bar to 90%
            // handle pre-game action

            setProgress(100); // set progress bar to 100%
            // handle game action
        }



        private void StopServer()
        {
            btnStopServer.Enabled = false;
            btnRestartServer.Enabled = false;
            btnStartServer.Enabled = true;

            // signal listener thread to finish
            _stopListenForRequests = true;

            updateStatus("Server Stopped\n");
            setProgress(50);
            setProgress(0);
        }


        public void setProgress(int percent)
        {
            // updates the server status progress bar to the percent passed as arg
            progressBar1.Value = percent;
        }
    }


    public class NetworkSocket
    {
        FormMainForm formMain;
        Socket socket = null;
        IPEndPoint endpoint;

        // constructors
        public NetworkSocket(FormMainForm fmf)
        {   
            formMain = fmf;
        }

        public void Dispose()
        {
            // close the socket before disposing of it
            socket.Close();
            socket = null;
        }

        public void Create()
        {
            // create a socket on the server on port 6001 on all network address
            endpoint = new IPEndPoint(IPAddress.Any, 6001);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            Bind();
            Listen();
        }

        // polling functions
        public bool PollRead()
        {
            return socket.Poll(0, SelectMode.SelectRead);
        }

        public bool PollWrite()
        {
            return socket.Poll(0, SelectMode.SelectWrite);
        }

        public bool PollError()
        {
            return socket.Poll(0, SelectMode.SelectError);
        }

        // send and receive (byte)
        public void Send(byte[] msg)
        {
            socket.Send(msg, SocketFlags.None);
        }

        public int Receive(byte[] msg, int size)
        {
            return socket.Receive(msg, size, SocketFlags.None); 
        }

        // listen, accept, bind, close
        private void Listen()
        {
            try { socket.Listen(4); }
            catch (SocketException e)
            {
                if (this.formMain.InvokeRequired)
                {
                    formMain.Invoke(formMain.updateStatusDelegate1,
                        new object[] { "NetworkSocket::Listen() Source : " + e.Source + "\n" });
                    formMain.Invoke(formMain.updateStatusDelegate1,
                       new object[] { "NetworkSocket::Listen() Message : " + e.Message + "\n" });
                }
                else
                {
                    formMain.updateStatus("NetworkSocket::Listen() Source : " + e.Source);
                    formMain.updateStatus("NetworkSocket::Listen() Message : " + e.Message);
                }
            }
            catch (Exception e)
            {
                if (this.formMain.InvokeRequired)
                {
                    formMain.Invoke(formMain.updateStatusDelegate1,
                        new object[] { "NetworkSocket::Listen() Source : " + e.Source + "\n" });
                    formMain.Invoke(formMain.updateStatusDelegate1,
                       new object[] { "NetworkSocket::Listen() Message : " + e.Message + "\n" });
                }
                else
                {
                    formMain.updateStatus("NetworkSocket::Listen() Source : " + e.Source);
                    formMain.updateStatus("NetworkSocket::Listen() Message : " + e.Message);
                }
            }
        }

        public NetworkSocket Accept()
        {
            Socket socketnew = null;
            NetworkSocket nwsnew = null;

            if (socket.Poll(0, SelectMode.SelectRead))
            {
                try { socketnew = socket.Accept(); }
                catch (SocketException e)
                {
                    if (this.formMain.InvokeRequired)
                    {
                        formMain.Invoke(formMain.updateStatusDelegate1,
                            new object[] { "NetworkSocket::Accept() Source : " + e.Source + "\n" });
                        formMain.Invoke(formMain.updateStatusDelegate1,
                           new object[] { "NetworkSocket::Accept() Message : " + e.Message + "\n" });
                    }
                    else
                    {
                        formMain.updateStatus("NetworkSocket::Accept() Source : " + e.Source);
                        formMain.updateStatus("NetworkSocket::Accept() Message : " + e.Message);
                    }
                }
                catch (Exception e)
                {
                    if (this.formMain.InvokeRequired)
                    {
                        formMain.Invoke(formMain.updateStatusDelegate1,
                            new object[] { "NetworkSocket::Accept() Source : " + e.Source + "\n" });
                        formMain.Invoke(formMain.updateStatusDelegate1,
                           new object[] { "NetworkSocket::Accept() Message : " + e.Message + "\n" });
                    }
                    else
                    {
                        formMain.updateStatus("NetworkSocket::Accept() Source : " + e.Source);
                        formMain.updateStatus("NetworkSocket::Accept() Message : " + e.Message);
                    }
                }

                nwsnew = new NetworkSocket(formMain);
                nwsnew.socket = socketnew;
                nwsnew.endpoint = endpoint;
            }

            return nwsnew;
        }

        private void Bind()
        {
            try { socket.Bind(endpoint); } 
            catch(SocketException e) 
            {
                if (this.formMain.InvokeRequired)
                {
                    formMain.Invoke(formMain.updateStatusDelegate1,
                        new object[] { "NetworkSocket::Bind() Source : " + e.Source + "\n" });
                    formMain.Invoke(formMain.updateStatusDelegate1,
                       new object[] { "NetworkSocket::Bind() Message : " + e.Message + "\n" });
                }
                else
                {
                    formMain.updateStatus("NetworkSocket::Bind() Source : " + e.Source);
                    formMain.updateStatus("NetworkSocket::Bind() Message : " + e.Message);
                }
            }
            catch(Exception e)
            {
                if (this.formMain.InvokeRequired)
                {
                    formMain.Invoke(formMain.updateStatusDelegate1,
                        new object[] { "NetworkSocket::Bind() Source : " + e.Source + "\n" });
                    formMain.Invoke(formMain.updateStatusDelegate1,
                       new object[] { "NetworkSocket::Bind() Message : " + e.Message + "\n" });
                }
                else
                {
                    formMain.updateStatus("NetworkSocket::Bind() Source : " + e.Source);
                    formMain.updateStatus("NetworkSocket::Bind() Message : " + e.Message);
                }
            }
        }

        private void Close()
        {
            socket.Close();
        }
    }
}
